<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
        <title>《Go安全指南》 - Lucklyの博客 - 在阅读中遇见自己</title><meta name="Description" content="早起的年轻人做的网站"><meta property="og:title" content="《Go安全指南》" />
<meta property="og:description" content="
本文主要介绍面向开发人员梳理的代码安全指南，旨在梳理API层面的风险点并提供详实可行的安全编码方案。
" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" />
<meta property="og:image" content="https://luckly.work/logo.png"/>
<meta property="article:published_time" content="2021-01-25T22:44:54+08:00" />
<meta property="article:modified_time" content="2021-01-25T22:44:54+08:00" />
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://luckly.work/logo.png"/>

<meta name="twitter:title" content="《Go安全指南》"/>
<meta name="twitter:description" content="
本文主要介绍面向开发人员梳理的代码安全指南，旨在梳理API层面的风险点并提供详实可行的安全编码方案。
"/>
<meta name="application-name" content="LoveIt">
<meta name="apple-mobile-web-app-title" content="LoveIt"><meta name="theme-color" content="#ffffff"><meta name="msapplication-TileColor" content="#da532c"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"><link rel="manifest" href="/site.webmanifest"><link rel="canonical" href="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" /><link rel="prev" href="https://luckly.work/%E9%95%BF%E6%81%A8%E6%AD%8C/" /><link rel="next" href="https://luckly.work/%E5%A6%82%E4%BD%95%E5%88%B6%E5%AE%9A%E8%AE%A1%E5%88%92/" /><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css"><link rel="stylesheet" href="/css/style.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.7.2/animate.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "《Go安全指南》",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/luckly.work\/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97\/"
        },"image": [{
                            "@type": "ImageObject",
                            "url": "https:\/\/luckly.work\/images\/Apple-Devices-Preview.png",
                            "width":  3200 ,
                            "height":  2048 
                        }],"genre": "posts","keywords": "Go","wordcount":  7693 ,
        "url": "https:\/\/luckly.work\/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97\/","datePublished": "2021-01-25T22:44:54+08:00","dateModified": "2021-01-25T22:44:54+08:00","license": "This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.","publisher": {
            "@type": "Organization",
            "name": "xxxx","logo": {
                    "@type": "ImageObject",
                    "url": "https:\/\/luckly.work\/images\/avatar.png",
                    "width":  528 ,
                    "height":  560 
                }},"author": {
                "@type": "Person",
                "name": "早起的年轻人"
            },"description": ""
    }
    </script></head>
    <body header-desktop="fixed" header-mobile="auto"><script type="text/javascript">(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('auto' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : 'auto' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="Lucklyの博客 - 在阅读中遇见自己"><span class="header-title-pre"><i class='far fa-kiss-wink-heart fa-fw'></i></span><span id="id-1" class="typeit"></span></a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> 所有文章 </a><a class="menu-item" href="/categories/flutter/"> Flutter </a><a class="menu-item" href="/categories/read/"> 读书笔记 </a><a class="menu-item" href="/categories/go/"> Go </a><a class="menu-item" href="/tags/"> 标签 </a><a class="menu-item" href="/categories/"> 分类 </a><a class="menu-item" href="/about/"> 关于 </a><a class="menu-item" href="https://github.com/ITmxs" title="GitHub" rel="noopener noreffer" target="_blank"><i class='fab fa-github fa-fw'></i> Github </a><a class="menu-item" href="/friend/"><i class='fas fa-user-friends'></i> 友链 </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-desktop">
                        <a href="javascript:void(0);" class="search-button search-toggle" id="search-toggle-desktop" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="javascript:void(0);" class="search-button search-clear" id="search-clear-desktop" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="Lucklyの博客 - 在阅读中遇见自己"><span class="header-title-pre"><i class='far fa-kiss-wink-heart fa-fw'></i></span><span id="id-2" class="typeit"></span></a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-mobile">
                        <a href="javascript:void(0);" class="search-button search-toggle" id="search-toggle-mobile" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="javascript:void(0);" class="search-button search-clear" id="search-clear-mobile" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="javascript:void(0);" class="search-cancel" id="search-cancel-mobile">
                        取消
                    </a>
                </div><a class="menu-item" href="/posts/" title="">所有文章</a><a class="menu-item" href="/categories/flutter/" title="">Flutter</a><a class="menu-item" href="/categories/read/" title="">读书笔记</a><a class="menu-item" href="/categories/go/" title="">Go</a><a class="menu-item" href="/tags/" title="">标签</a><a class="menu-item" href="/categories/" title="">分类</a><a class="menu-item" href="/about/" title="">关于</a><a class="menu-item" href="https://github.com/ITmxs" title="GitHub" rel="noopener noreffer" target="_blank"><i class='fab fa-github fa-fw'></i>Github</a><a class="menu-item" href="/friend/" title=""><i class='fas fa-user-friends'></i>友链</a><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                <i class="fas fa-adjust fa-fw"></i>
            </a></div>
    </div>
</header>
<div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div>
<main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">目录</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single"><h1 class="single-title animated flipInX">《Go安全指南》</h1><div class="post-meta">
            <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>早起的年轻人</a></span>&nbsp;<span class="post-category">收录于 <a href="/categories/go/"><i class="far fa-folder fa-fw"></i>Go</a></span></div>
            <div class="post-meta-line"><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2021-01-25">2021-01-25</time>&nbsp;<i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 7693 字&nbsp;
                <i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 16 分钟&nbsp;</div>
        </div><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png"
        data-srcset="https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png, https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png 1.5x, https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png 2x"
        data-sizes="auto"
        alt="https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png"
        title="https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png" /></div><div class="details toc" id="toc-static"  kept="">
                <div class="details-summary toc-title">
                    <span>目录</span>
                    <span><i class="details-icon fas fa-angle-right"></i></span>
                </div>
                <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#通用类">通用类</a>
      <ul>
        <li><a href="#1-代码实现类">1. 代码实现类</a>
          <ul>
            <li><a href="#11-内存管理">1.1 内存管理</a>
              <ul>
                <li><a href="#111必须切片长度校验">1.1.1【必须】切片长度校验</a></li>
                <li><a href="#112必须nil指针判断">1.1.2【必须】nil指针判断</a></li>
                <li><a href="#113必须整数安全">1.1.3【必须】整数安全</a></li>
                <li><a href="#114必须make分配长度验证">1.1.4【必须】make分配长度验证</a></li>
                <li><a href="#115必须禁止setfinalizer和指针循环引用同时使用">1.1.5【必须】禁止SetFinalizer和指针循环引用同时使用</a></li>
                <li><a href="#116必须禁止重复释放channel">1.1.6【必须】禁止重复释放channel</a></li>
                <li><a href="#117必须确保每个协程都能退出">1.1.7【必须】确保每个协程都能退出</a></li>
                <li><a href="#118推荐不使用unsafe包">1.1.8【推荐】不使用unsafe包</a></li>
                <li><a href="#119推荐不使用slice作为函数入参">1.1.9【推荐】不使用slice作为函数入参</a></li>
              </ul>
            </li>
            <li><a href="#12-文件操作">1.2 文件操作</a>
              <ul>
                <li><a href="#121必须-路径穿越检查">1.2.1【必须】 路径穿越检查</a></li>
                <li><a href="#122必须-文件访问权限">1.2.2【必须】 文件访问权限</a></li>
              </ul>
            </li>
            <li><a href="#13-系统接口">1.3 系统接口</a></li>
            <li><a href="#14-通信安全">1.4 通信安全</a>
              <ul>
                <li><a href="#141必须网络通信采用tls方式">1.4.1【必须】网络通信采用TLS方式</a></li>
                <li><a href="#142推荐tls启用证书验证">1.4.2【推荐】TLS启用证书验证</a></li>
              </ul>
            </li>
            <li><a href="#15-敏感数据保护">1.5 敏感数据保护</a>
              <ul>
                <li><a href="#151必须敏感信息访问">1.5.1【必须】敏感信息访问</a></li>
                <li><a href="#152必须敏感数据输出">1.5.2【必须】敏感数据输出</a></li>
                <li><a href="#153必须敏感数据存储">1.5.3【必须】敏感数据存储</a></li>
                <li><a href="#154必须异常处理和日志记录">1.5.4【必须】异常处理和日志记录</a></li>
              </ul>
            </li>
            <li><a href="#16-加密解密">1.6 加密解密</a>
              <ul>
                <li><a href="#161必须不得硬编码密码密钥">1.6.1【必须】不得硬编码密码/密钥</a></li>
                <li><a href="#162必须密钥存储安全">1.6.2【必须】密钥存储安全</a></li>
                <li><a href="#163推荐不使用弱密码算法">1.6.3【推荐】不使用弱密码算法</a></li>
              </ul>
            </li>
            <li><a href="#17-正则表达式">1.7 正则表达式</a>
              <ul>
                <li><a href="#171推荐使用regexp进行正则表达式匹配">1.7.1【推荐】使用regexp进行正则表达式匹配</a></li>
              </ul>
            </li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#后台类">后台类</a>
      <ul>
        <li><a href="#1-代码实现类-1">1 代码实现类</a>
          <ul>
            <li><a href="#11-输入校验">1.1 输入校验</a>
              <ul>
                <li><a href="#111必须按类型进行数据校验">1.1.1【必须】按类型进行数据校验</a></li>
              </ul>
            </li>
            <li><a href="#12-sql操作">1.2 SQL操作</a>
              <ul>
                <li><a href="#121必须sql语句默认使用预编译并绑定变量">1.2.1【必须】SQL语句默认使用预编译并绑定变量</a></li>
              </ul>
            </li>
            <li><a href="#13-网络请求">1.3 网络请求</a>
              <ul>
                <li><a href="#131必须资源请求过滤验证">1.3.1【必须】资源请求过滤验证</a></li>
              </ul>
            </li>
            <li><a href="#14-服务器端渲染">1.4 服务器端渲染</a>
              <ul>
                <li><a href="#141必须模板渲染过滤验证">1.4.1【必须】模板渲染过滤验证</a></li>
              </ul>
            </li>
            <li><a href="#15-web跨域">1.5 Web跨域</a>
              <ul>
                <li><a href="#151必须跨域资源共享cors限制请求来源">1.5.1【必须】跨域资源共享CORS限制请求来源</a></li>
              </ul>
            </li>
            <li><a href="#16-响应输出">1.6 响应输出</a>
              <ul>
                <li><a href="#161-必须设置正确的http响应包类型">1.6.1 【必须】设置正确的HTTP响应包类型</a></li>
                <li><a href="#162-必须添加安全响应头">1.6.2 【必须】添加安全响应头</a></li>
                <li><a href="#163必须外部输入拼接到http响应头中需进行过滤">1.6.3【必须】外部输入拼接到HTTP响应头中需进行过滤</a></li>
                <li><a href="#164必须外部输入拼接到response页面前进行编码处理">1.6.4【必须】外部输入拼接到response页面前进行编码处理</a></li>
              </ul>
            </li>
            <li><a href="#17-会话管理">1.7 会话管理</a>
              <ul>
                <li><a href="#171必须安全维护session信息">1.7.1【必须】安全维护session信息</a></li>
                <li><a href="#172必须csrf防护">1.7.2【必须】CSRF防护</a></li>
              </ul>
            </li>
            <li><a href="#18-访问控制">1.8 访问控制</a>
              <ul>
                <li><a href="#181必须默认鉴权">1.8.1【必须】默认鉴权</a></li>
              </ul>
            </li>
            <li><a href="#19-并发保护">1.9 并发保护</a>
              <ul>
                <li><a href="#191必须禁止在闭包中直接调用循环变量">1.9.1【必须】禁止在闭包中直接调用循环变量</a></li>
                <li><a href="#192必须禁止并发写map">1.9.2【必须】禁止并发写map</a></li>
                <li><a href="#193必须确保并发安全">1.9.3【必须】确保并发安全</a></li>
              </ul>
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</nav></div>
            </div><div class="content" id="content"><blockquote>
<p>本文主要介绍面向开发人员梳理的代码安全指南，旨在梳理API层面的风险点并提供详实可行的安全编码方案。</p>
</blockquote>
<!-- markdown="1" is required for GitHub Pages to render the TOC properly. -->
<details markdown="1">
  <summary>目录</summary>
<ul>
<li>
<p><a href="#1" rel="">1 通用类</a></p>
<ul>
<li><a href="#1.1" rel="">I. 代码实现</a>
<ul>
<li><a href="#1.1.1" rel="">1.1 内存管理</a></li>
<li><a href="#1.1.2" rel="">1.2 文件操作</a></li>
<li><a href="#1.1.3" rel="">1.3 系统接口</a></li>
<li><a href="#1.1.4" rel="">1.4 通信安全</a></li>
<li><a href="#1.1.5" rel="">1.5 敏感数据保护</a></li>
<li><a href="#1.1.6" rel="">1.6 加密解密</a></li>
<li><a href="#1.1.7" rel="">1.7 正则表达式</a></li>
</ul>
</li>
</ul>
</li>
<li>
<p><a href="#2" rel="">2 后台类</a></p>
<ul>
<li><a href="#2.1" rel="">I. 代码实现</a>
<ul>
<li><a href="#2.1.1" rel="">1.1 输入校验</a></li>
<li><a href="#2.1.2" rel="">1.2 SQL操作</a></li>
<li><a href="#2.1.3" rel="">1.3 网络请求</a></li>
<li><a href="#2.1.4" rel="">1.4 服务器端渲染</a></li>
<li><a href="#2.1.5" rel="">1.5 Web跨域</a></li>
<li><a href="#2.1.6" rel="">1.6 响应输出</a></li>
<li><a href="#2.1.7" rel="">1.7 会话管理</a></li>
<li><a href="#2.1.8" rel="">1.8 访问控制</a></li>
<li><a href="#2.1.9" rel="">1.9 并发保护</a>
</details>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><a id="1"></a></p>
<h1 id="通用类">通用类</h1>
<p><a id="1.1"></a></p>
<h2 id="1-代码实现类">1. 代码实现类</h2>
<p><a id="1.1.1"></a></p>
<h3 id="11-内存管理">1.1 内存管理</h3>
<h4 id="111必须切片长度校验">1.1.1【必须】切片长度校验</h4>
<ul>
<li>在对slice进行操作时，必须判断长度是否合法，防止程序panic</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad: 未判断data的长度，可导致 index out of range 
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">decode</span><span class="p">(</span><span class="nx">data</span> <span class="p">[]</span> <span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;F&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;U&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;Z&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;Z&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;E&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;R&#39;</span> <span class="p">{</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Bad&#34;</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">true</span>
    <span class="p">}</span>
  
    <span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>

<span class="c1">// bad: slice bounds out of range
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">slice</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">}</span>
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">slice</span><span class="p">[:</span><span class="mi">10</span><span class="p">])</span> 
<span class="p">}</span>

<span class="c1">// good: 使用data前应判断长度是否合法 
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">decode</span><span class="p">(</span><span class="nx">data</span> <span class="p">[]</span> <span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span> <span class="p">{</span>
        <span class="k">if</span> <span class="nx">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;F&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;U&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;Z&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;Z&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;E&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;R&#39;</span> <span class="p">{</span>
            <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Good&#34;</span><span class="p">)</span>
            <span class="k">return</span> <span class="kc">true</span>
        <span class="p">}</span>
    <span class="p">}</span>
	
    <span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="112必须nil指针判断">1.1.2【必须】nil指针判断</h4>
<ul>
<li>进行指针操作时，必须判断该指针是否为nil，防止程序panic，尤其在进行结构体Unmarshal时</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Packet</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="nx">PackeyType</span>        <span class="kt">uint8</span>
    <span class="nx">PackeyVersion</span>     <span class="kt">uint8</span>
    <span class="nx">Data</span>              <span class="o">*</span><span class="nx">Data</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Data</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="nx">Stat</span>    <span class="kt">uint8</span>
    <span class="nx">Len</span> 	<span class="kt">uint8</span>
    <span class="nx">Buf</span> 	<span class="p">[</span><span class="mi">8</span><span class="p">]</span><span class="kt">byte</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Packet</span><span class="p">)</span> <span class="nf">UnmarshalBinary</span><span class="p">(</span><span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="p">&lt;</span> <span class="mi">2</span> <span class="p">{</span>
       <span class="k">return</span> <span class="nx">io</span><span class="p">.</span><span class="nx">EOF</span>
    <span class="p">}</span>
  
    <span class="nx">p</span><span class="p">.</span><span class="nx">PackeyType</span> <span class="p">=</span> <span class="nx">b</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="nx">p</span><span class="p">.</span><span class="nx">PackeyVersion</span> <span class="p">=</span> <span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
  
    <span class="c1">// 若长度等于2，那么不会new Data
</span><span class="c1"></span>    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="p">&gt;</span> <span class="mi">2</span> <span class="p">{</span>
        <span class="nx">p</span><span class="p">.</span><span class="nx">Data</span> <span class="p">=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">Data</span><span class="p">)</span>
        <span class="c1">// Unmarshal(b[i:], p.Data)
</span><span class="c1"></span>    <span class="p">}</span>
  
    <span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>

<span class="c1">// bad: 未判断指针是否为nil
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">packet</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">Packet</span><span class="p">)</span>
    <span class="nx">data</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
    <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">packet</span><span class="p">.</span><span class="nf">UnmarshalBinary</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Failed to unmarshal packet&#34;</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Stat: %v\n&#34;</span><span class="p">,</span> <span class="nx">packet</span><span class="p">.</span><span class="nx">Data</span><span class="p">.</span><span class="nx">Stat</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// good: 判断Data指针是否未nil
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    
    <span class="nx">packet</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">Packet</span><span class="p">)</span>
    <span class="nx">data</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
    
    <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">packet</span><span class="p">.</span><span class="nf">UnmarshalBinary</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Failed to unmarshal packet&#34;</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="k">if</span> <span class="nx">packet</span><span class="p">.</span><span class="nx">Data</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Stat: %v\n&#34;</span><span class="p">,</span> <span class="nx">packet</span><span class="p">.</span><span class="nx">Data</span><span class="p">.</span><span class="nx">Stat</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="113必须整数安全">1.1.3【必须】整数安全</h4>
<ul>
<li>
<p>在进行数字运算操作时，需要做好长度限制，防止外部输入运算导致异常：</p>
<ul>
<li>确保无符号整数运算时不会反转</li>
<li>确保有符号整数运算时不会出现溢出</li>
<li>确保整型转换时不会出现截断错误</li>
<li>确保整型转换时不会出现符号错误</li>
</ul>
</li>
<li>
<p>以下场景必须严格进行长度限制：</p>
<ul>
<li>作为数组索引</li>
<li>作为对象的长度或者大小</li>
<li>作为数组的边界（如作为循环计数器）</li>
</ul>
</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad: 未限制长度，导致整数溢出
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">overflow</span><span class="p">(</span><span class="nx">numControlByUser</span> <span class="kt">int32</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">numInt</span> <span class="kt">int32</span> <span class="p">=</span> <span class="mi">0</span>
    <span class="nx">numInt</span> <span class="p">=</span> <span class="nx">numControlByUser</span> <span class="o">+</span> <span class="mi">1</span>
    <span class="c1">//对长度限制不当，导致整数溢出
</span><span class="c1"></span>    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;%d\n&#34;</span><span class="p">,</span> <span class="nx">numInt</span><span class="p">)</span>
    <span class="c1">//使用numInt，可能导致其他错误
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">overflow</span><span class="p">(</span><span class="mi">2147483647</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// good: 
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">overflow</span><span class="p">(</span><span class="nx">numControlByUser</span> <span class="kt">int32</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">numInt</span> <span class="kt">int32</span> <span class="p">=</span> <span class="mi">0</span>
    <span class="nx">numInt</span> <span class="p">=</span> <span class="nx">numControlByUser</span> <span class="o">+</span> <span class="mi">1</span>
    <span class="k">if</span> <span class="nx">numInt</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">{</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;integer overflow&#34;</span><span class="p">)</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span> 
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;integer ok&#34;</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">overflow</span><span class="p">(</span><span class="mi">2147483647</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="114必须make分配长度验证">1.1.4【必须】make分配长度验证</h4>
<ul>
<li>在进行make分配内存时，需要对外部可控的长度进行校验，防止程序panic。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nx">lenControlByUser</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">data</span><span class="p">[]</span> <span class="kt">byte</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">size</span> <span class="o">:=</span> <span class="nx">lenControlByUser</span>
    <span class="c1">//对外部传入的size，进行长度判断以免导致panic
</span><span class="c1"></span>    <span class="nx">buffer</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
    <span class="nb">copy</span><span class="p">(</span><span class="nx">buffer</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nx">lenControlByUser</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">data</span><span class="p">[]</span> <span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">){</span>
    <span class="nx">size</span> <span class="o">:=</span> <span class="nx">lenControlByUser</span>
    <span class="c1">//限制外部可控的长度大小范围
</span><span class="c1"></span>    <span class="k">if</span> <span class="nx">size</span> <span class="p">&gt;</span> <span class="mi">64</span><span class="o">*</span><span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;value too large&#34;</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="nx">buffer</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">size</span><span class="p">)</span>
    <span class="nb">copy</span><span class="p">(</span><span class="nx">buffer</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span>
    <span class="k">return</span> <span class="nx">buffer</span><span class="p">,</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="115必须禁止setfinalizer和指针循环引用同时使用">1.1.5【必须】禁止SetFinalizer和指针循环引用同时使用</h4>
<ul>
<li>当一个对象从被GC选中到移除内存之前，runtime.SetFinalizer()都不会执行，即使程序正常结束或者发生错误。由指针构成的“循环引用”虽然能被GC正确处理，但由于无法确定Finalizer依赖顺序，从而无法调用runtime.SetFinalizer()，导致目标对象无法变成可达状态，从而造成内存无法被回收。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="nx">Data</span>
    <span class="nx">a</span><span class="p">.</span><span class="nx">o</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">b</span>
    <span class="nx">b</span><span class="p">.</span><span class="nx">o</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">a</span>

    <span class="c1">//指针循环引用，SetFinalizer()无法正常调用
</span><span class="c1"></span>    <span class="nx">runtime</span><span class="p">.</span><span class="nf">SetFinalizer</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">a</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">d</span> <span class="o">*</span><span class="nx">Data</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;a %p final.\n&#34;</span><span class="p">,</span> <span class="nx">d</span><span class="p">)</span>
    <span class="p">})</span>
    <span class="nx">runtime</span><span class="p">.</span><span class="nf">SetFinalizer</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">b</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">d</span> <span class="o">*</span><span class="nx">Data</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;b %p final.\n&#34;</span><span class="p">,</span> <span class="nx">d</span><span class="p">)</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">{</span>
        <span class="nf">foo</span><span class="p">()</span>
        <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="116必须禁止重复释放channel">1.1.6【必须】禁止重复释放channel</h4>
<ul>
<li>重复释放一般存在于异常流程判断中，如果恶意攻击者构造出异常条件使程序重复释放channel，则会触发运行时恐慌，从而造成DoS攻击。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">c</span> <span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span>
    <span class="nx">err</span> <span class="o">:=</span> <span class="nf">processBusiness</span><span class="p">()</span>
    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
        <span class="nx">c</span> <span class="o">&lt;-</span> <span class="mi">0</span>
        <span class="nb">close</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="c1">// 重复释放channel
</span><span class="c1"></span>        <span class="k">return</span>
    <span class="p">}</span>
    <span class="nx">c</span> <span class="o">&lt;-</span> <span class="mi">1</span>
<span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">c</span> <span class="kd">chan</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="c1">// 使用defer延迟关闭channel
</span><span class="c1"></span>    <span class="nx">err</span> <span class="o">:=</span> <span class="nf">processBusiness</span><span class="p">()</span>
    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
        <span class="nx">c</span> <span class="o">&lt;-</span> <span class="mi">0</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="nx">c</span> <span class="o">&lt;-</span> <span class="mi">1</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="117必须确保每个协程都能退出">1.1.7【必须】确保每个协程都能退出</h4>
<ul>
<li>启动一个协程就会做一个入栈操作，在系统不退出的情况下，协程也没有设置退出条件，则相当于协程失去了控制，它占用的资源无法回收，可能会导致内存泄露。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad: 协程没有设置退出条件
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">doWaiter</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">second</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">{</span>
        <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="nx">second</span><span class="p">)</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="s">&#34; is ready!&#34;</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="118推荐不使用unsafe包">1.1.8【推荐】不使用unsafe包</h4>
<ul>
<li>由于unsafe包绕过了 Golang 的内存安全原则，一般来说使用该库是不安全的，可导致内存破坏，尽量避免使用该包。若必须要使用unsafe操作指针，必须做好安全校验。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad: 通过unsafe操作原始指针
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">unsafePointer</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">b</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="nx">foo</span> <span class="o">:=</span> <span class="p">(</span><span class="o">*</span><span class="kt">int</span><span class="p">)(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="nb">uintptr</span><span class="p">(</span><span class="nx">unsafe</span><span class="p">.</span><span class="nf">Pointer</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">b</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span> <span class="o">+</span> <span class="nb">uintptr</span><span class="p">(</span><span class="mh">0xfffffffe</span><span class="p">)))</span>
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="o">*</span><span class="nx">foo</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// [signal SIGSEGV: segmentation violation code=0x1 addr=0xc100068f55 pc=0x49142b]
</span></code></pre></td></tr></table>
</div>
</div><h4 id="119推荐不使用slice作为函数入参">1.1.9【推荐】不使用slice作为函数入参</h4>
<ul>
<li>slice是引用类型，在作为函数入参时采用的是地址传递，对slice的修改也会影响原始数据</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go">  <span class="c1">// bad
</span><span class="c1"></span>  <span class="c1">// slice作为函数入参时是地址传递
</span><span class="c1"></span>  <span class="kd">func</span> <span class="nf">modify</span><span class="p">(</span><span class="nx">array</span> <span class="p">[]</span><span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="mi">10</span> <span class="c1">// 对入参slice的元素修改会影响原始数据
</span><span class="c1"></span>  <span class="p">}</span>
  
  <span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
      <span class="nx">array</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">}</span>
  
      <span class="nf">modify</span><span class="p">(</span><span class="nx">array</span><span class="p">)</span>
      <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">array</span><span class="p">)</span> <span class="c1">// output：[10 2 3 4 5]
</span><span class="c1"></span>  <span class="p">}</span>

  <span class="c1">// good
</span><span class="c1"></span>  <span class="c1">// 数组作为函数入参时，而不是slice
</span><span class="c1"></span>  <span class="kd">func</span> <span class="nf">modify</span><span class="p">(</span><span class="nx">array</span> <span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="mi">10</span>
  <span class="p">}</span>

  <span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
      <span class="c1">// 传入数组，注意数组与slice的区别
</span><span class="c1"></span>      <span class="nx">array</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">}</span>
  
      <span class="nf">modify</span><span class="p">(</span><span class="nx">array</span><span class="p">)</span>
      <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">array</span><span class="p">)</span>
  <span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="1.1.2"></a></p>
<h3 id="12-文件操作">1.2 文件操作</h3>
<h4 id="121必须-路径穿越检查">1.2.1【必须】 路径穿越检查</h4>
<ul>
<li>在进行文件操作时，如果对外部传入的文件名未做限制，可能导致任意文件读取或者任意文件写入，严重可能导致代码执行。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad: 任意文件读取
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
	<span class="nx">path</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nf">Query</span><span class="p">()[</span><span class="s">&#34;path&#34;</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>

	<span class="c1">// 未过滤文件路径，可能导致任意文件读取
</span><span class="c1"></span>	<span class="nx">data</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
	<span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>

	<span class="c1">// 对外部传入的文件名变量，还需要验证是否存在../等路径穿越的文件名
</span><span class="c1"></span>	<span class="nx">data</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span><span class="nx">filepath</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">&#34;/home/user/&#34;</span><span class="p">,</span> <span class="nx">path</span><span class="p">))</span>
	<span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// bad: 任意文件写入
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">unzip</span><span class="p">(</span><span class="nx">f</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
	<span class="nx">r</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">zip</span><span class="p">.</span><span class="nf">OpenReader</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span>
	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">r</span><span class="p">.</span><span class="nx">File</span> <span class="p">{</span>
		<span class="nx">p</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">filepath</span><span class="p">.</span><span class="nf">Abs</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span>
		<span class="c1">// 未验证压缩文件名，可能导致../等路径穿越，任意文件路径写入
</span><span class="c1"></span>		<span class="nx">ioutil</span><span class="p">.</span><span class="nf">WriteFile</span><span class="p">(</span><span class="nx">p</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;present&#34;</span><span class="p">),</span> <span class="mo">0640</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="c1">// good: 检查压缩的文件名是否包含..路径穿越特征字符，防止任意写入
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">unzipGood</span><span class="p">(</span><span class="nx">f</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
	<span class="nx">r</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">zip</span><span class="p">.</span><span class="nf">OpenReader</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;read zip file fail&#34;</span><span class="p">)</span>
		<span class="k">return</span> <span class="kc">false</span>
	<span class="p">}</span>
	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">r</span><span class="p">.</span><span class="nx">File</span> <span class="p">{</span>
		<span class="nx">p</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">filepath</span><span class="p">.</span><span class="nf">Abs</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span>
		<span class="k">if</span> <span class="p">!</span><span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">p</span><span class="p">,</span> <span class="s">&#34;..&#34;</span><span class="p">)</span> <span class="p">{</span>
			<span class="nx">ioutil</span><span class="p">.</span><span class="nf">WriteFile</span><span class="p">(</span><span class="nx">p</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;present&#34;</span><span class="p">),</span> <span class="mo">0640</span><span class="p">)</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="122必须-文件访问权限">1.2.2【必须】 文件访问权限</h4>
<ul>
<li>根据创建文件的敏感性设置不同级别的访问权限，以防止敏感数据被任意权限用户读取。例如，设置文件权限为：<code>-rw-r-----</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="nx">ioutil</span><span class="p">.</span><span class="nf">WriteFile</span><span class="p">(</span><span class="nx">p</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;present&#34;</span><span class="p">),</span> <span class="mo">0640</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="1.1.3"></a></p>
<h3 id="13-系统接口">1.3 系统接口</h3>
<p><strong>1.3.1【必须】命令执行检查</strong></p>
<ul>
<li>使用<code>exec.Command</code>、<code>exec.CommandContext</code>、<code>syscall.StartProcess</code>、<code>os.StartProcess</code>等函数时，第一个参数（path）直接取外部输入值时，应使用白名单限定可执行的命令范围，不允许传入<code>bash</code>、<code>cmd</code>、<code>sh</code>等命令；</li>
<li>使用<code>exec.Command</code>、<code>exec.CommandContext</code>等函数时，通过<code>bash</code>、<code>cmd</code>、<code>sh</code>等创建shell，-c后的参数（arg）拼接外部输入，应过滤\n  $  &amp;  ;  |  '  &quot;  ( )  `等潜在恶意字符；</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">userInputedVal</span> <span class="o">:=</span> <span class="s">&#34;&amp;&amp; echo &#39;hello&#39;&#34;</span> <span class="c1">// 假设外部传入该变量值
</span><span class="c1"></span>	<span class="nx">cmdName</span> <span class="o">:=</span> <span class="s">&#34;ping &#34;</span> <span class="o">+</span> <span class="nx">userInputedVal</span>

	<span class="c1">//未判断外部输入是否存在命令注入字符，结合sh可造成命令注入
</span><span class="c1"></span>	<span class="nx">cmd</span> <span class="o">:=</span> <span class="nx">exec</span><span class="p">.</span><span class="nf">Command</span><span class="p">(</span><span class="s">&#34;sh&#34;</span><span class="p">,</span> <span class="s">&#34;-c&#34;</span><span class="p">,</span> <span class="nx">cmdName</span><span class="p">)</span>
	<span class="nx">output</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">CombinedOutput</span><span class="p">()</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">output</span><span class="p">))</span>

	<span class="nx">cmdName</span> <span class="o">:=</span> <span class="s">&#34;ls&#34;</span>
	<span class="c1">//未判断外部输入是否是预期命令
</span><span class="c1"></span>	<span class="nx">cmd</span> <span class="o">:=</span> <span class="nx">exec</span><span class="p">.</span><span class="nf">Command</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">)</span>
	<span class="nx">output</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">CombinedOutput</span><span class="p">()</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">output</span><span class="p">))</span>
<span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">checkIllegal</span><span class="p">(</span><span class="nx">cmdName</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
	<span class="k">if</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;&amp;&#34;</span><span class="p">)</span> <span class="o">||</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;|&#34;</span><span class="p">)</span> <span class="o">||</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;;&#34;</span><span class="p">)</span> <span class="o">||</span>
		<span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;$&#34;</span><span class="p">)</span> <span class="o">||</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;&#39;&#34;</span><span class="p">)</span> <span class="o">||</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;`&#34;</span><span class="p">)</span> <span class="o">||</span>
		<span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;(&#34;</span><span class="p">)</span> <span class="o">||</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;)&#34;</span><span class="p">)</span> <span class="o">||</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">,</span> <span class="s">&#34;\&#34;&#34;</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kc">true</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">userInputedVal</span> <span class="o">:=</span> <span class="s">&#34;&amp;&amp; echo &#39;hello&#39;&#34;</span>
	<span class="nx">cmdName</span> <span class="o">:=</span> <span class="s">&#34;ping &#34;</span> <span class="o">+</span> <span class="nx">userInputedVal</span>

	<span class="k">if</span> <span class="nf">checkIllegal</span><span class="p">(</span><span class="nx">cmdName</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 检查传给sh的命令是否有特殊字符
</span><span class="c1"></span>		<span class="k">return</span> <span class="c1">// 存在特殊字符直接return
</span><span class="c1"></span>	<span class="p">}</span>

	<span class="nx">cmd</span> <span class="o">:=</span> <span class="nx">exec</span><span class="p">.</span><span class="nf">Command</span><span class="p">(</span><span class="s">&#34;sh&#34;</span><span class="p">,</span> <span class="s">&#34;-c&#34;</span><span class="p">,</span> <span class="nx">cmdName</span><span class="p">)</span>
	<span class="nx">output</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">CombinedOutput</span><span class="p">()</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">output</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="1.1.4"></a></p>
<h3 id="14-通信安全">1.4 通信安全</h3>
<h4 id="141必须网络通信采用tls方式">1.4.1【必须】网络通信采用TLS方式</h4>
<ul>
<li>明文传输的通信协议目前已被验证存在较大安全风险，被中间人劫持后可能导致许多安全风险，因此必须采用至少TLS的安全通信方式保证通信安全，例如gRPC/Websocket都使用TLS1.3。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="kd">func</span> <span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Add</span><span class="p">(</span><span class="s">&#34;Strict-Transport-Security&#34;</span><span class="p">,</span> <span class="s">&#34;max-age=63072000; includeSubDomains&#34;</span><span class="p">)</span>
    <span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;This is an example server.\n&#34;</span><span class="p">))</span>
  <span class="p">})</span>

  <span class="c1">//服务器配置证书与私钥
</span><span class="c1"></span>  <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServeTLS</span><span class="p">(</span><span class="s">&#34;:443&#34;</span><span class="p">,</span> <span class="s">&#34;yourCert.pem&#34;</span><span class="p">,</span> <span class="s">&#34;yourKey.pem&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="142推荐tls启用证书验证">1.4.2【推荐】TLS启用证书验证</h4>
<ul>
<li>TLS证书应当是有效的、未过期的，且配置正确的域名，生产环境的服务端应启用证书验证。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;crypto/tls&#34;</span>
	<span class="s">&#34;net/http&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">doAuthReq</span><span class="p">(</span><span class="nx">authReq</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span> <span class="p">{</span>
	<span class="nx">tr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Transport</span><span class="p">{</span>
		<span class="nx">TLSClientConfig</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
	<span class="p">}</span>
	<span class="nx">client</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Transport</span><span class="p">:</span> <span class="nx">tr</span><span class="p">}</span>
	<span class="nx">res</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">authReq</span><span class="p">)</span>
	<span class="k">return</span> <span class="nx">res</span>
<span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;crypto/tls&#34;</span>
	<span class="s">&#34;net/http&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">doAuthReq</span><span class="p">(</span><span class="nx">authReq</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span> <span class="p">{</span>
	<span class="nx">tr</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Transport</span><span class="p">{</span>
		<span class="nx">TLSClientConfig</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">tls</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="nx">InsecureSkipVerify</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
	<span class="p">}</span>
	<span class="nx">client</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Transport</span><span class="p">:</span> <span class="nx">tr</span><span class="p">}</span>
	<span class="nx">res</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">authReq</span><span class="p">)</span>
	<span class="k">return</span> <span class="nx">res</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="1.1.5"></a></p>
<h3 id="15-敏感数据保护">1.5 敏感数据保护</h3>
<h4 id="151必须敏感信息访问">1.5.1【必须】敏感信息访问</h4>
<ul>
<li>禁止将敏感信息硬编码在程序中，既可能会将敏感信息暴露给攻击者，也会增加代码管理和维护的难度</li>
<li>使用配置中心系统统一托管密钥等敏感信息</li>
</ul>
<h4 id="152必须敏感数据输出">1.5.2【必须】敏感数据输出</h4>
<ul>
<li>只输出必要的最小数据集，避免多余字段暴露引起敏感信息泄露</li>
<li>不能在日志保存密码（包括明文密码和密文密码）、密钥和其它敏感信息</li>
<li>对于必须输出的敏感信息，必须进行合理脱敏展示</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">serve</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/register&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">r</span><span class="p">.</span><span class="nf">ParseForm</span><span class="p">()</span>
		<span class="nx">user</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">)</span>
		<span class="nx">pw</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;password&#34;</span><span class="p">)</span>

		<span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Registering new user %s with password %s.\n&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">pw</span><span class="p">)</span>
	<span class="p">})</span>
	<span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:80&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">serve1</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/register&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">r</span><span class="p">.</span><span class="nf">ParseForm</span><span class="p">()</span>
		<span class="nx">user</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">)</span>
		<span class="nx">pw</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;password&#34;</span><span class="p">)</span>

		<span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;Registering new user %s.\n&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">)</span>

		<span class="c1">// ...
</span><span class="c1"></span>		<span class="nf">use</span><span class="p">(</span><span class="nx">pw</span><span class="p">)</span>
	<span class="p">})</span>
	<span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:80&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><ul>
<li>避免通过GET方法、代码注释、自动填充、缓存等方式泄露敏感信息</li>
</ul>
<h4 id="153必须敏感数据存储">1.5.3【必须】敏感数据存储</h4>
<ul>
<li>敏感数据应使用SHA2、RSA等算法进行加密存储</li>
<li>敏感数据应使用独立的存储层，并在访问层开启访问控制</li>
<li>包含敏感信息的临时文件或缓存一旦不再需要应立刻删除</li>
</ul>
<h4 id="154必须异常处理和日志记录">1.5.4【必须】异常处理和日志记录</h4>
<ul>
<li>应合理使用panic、recover、defer处理系统异常，避免出错信息输出到前端</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="k">defer</span> <span class="kd">func</span> <span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
            <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Recovered in start()&#34;</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}()</span>
</code></pre></td></tr></table>
</div>
</div><ul>
<li>对外环境禁止开启debug模式，或将程序运行日志输出到前端</li>
</ul>
<p>错误例子：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-bash" data-lang="bash">dlv --listen<span class="o">=</span>:2345 --headless<span class="o">=</span><span class="nb">true</span> --api-version<span class="o">=</span><span class="m">2</span> debug test.go
</code></pre></td></tr></table>
</div>
</div><p>正确例子：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-bash" data-lang="bash">dlv debug test.go
</code></pre></td></tr></table>
</div>
</div><p><a id="1.1.6"></a></p>
<h3 id="16-加密解密">1.6 加密解密</h3>
<h4 id="161必须不得硬编码密码密钥">1.6.1【必须】不得硬编码密码/密钥</h4>
<ul>
<li>在进行用户登陆，加解密算法等操作时，不得在代码里硬编码密钥或密码，可通过变换算法或者配置等方式设置密码或者密钥。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">const</span> <span class="p">(</span>
	<span class="nx">user</span>     <span class="p">=</span> <span class="s">&#34;dbuser&#34;</span>
	<span class="nx">password</span> <span class="p">=</span> <span class="s">&#34;s3cretp4ssword&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">connect</span><span class="p">()</span> <span class="o">*</span><span class="nx">sql</span><span class="p">.</span><span class="nx">DB</span> <span class="p">{</span>
	<span class="nx">connStr</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;postgres://%s:%s@localhost/pqgotest&#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">password</span><span class="p">)</span>
	<span class="nx">db</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sql</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="s">&#34;postgres&#34;</span><span class="p">,</span> <span class="nx">connStr</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="nx">db</span>
<span class="p">}</span>

<span class="c1">// bad
</span><span class="c1"></span><span class="kd">var</span> <span class="p">(</span>
	<span class="nx">commonkey</span> <span class="p">=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;0123456789abcdef&#34;</span><span class="p">)</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">AesEncrypt</span><span class="p">(</span><span class="nx">plaintext</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="nx">block</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">aes</span><span class="p">.</span><span class="nf">NewCipher</span><span class="p">(</span><span class="nx">commonkey</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">err</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="162必须密钥存储安全">1.6.2【必须】密钥存储安全</h4>
<ul>
<li>在使用对称密码算法时，需要保护好加密密钥。当算法涉及敏感、业务数据时，可通过非对称算法协商加密密钥。其他较为不敏感的数据加密，可以通过变换算法等方式保护密钥。</li>
</ul>
<h4 id="163推荐不使用弱密码算法">1.6.3【推荐】不使用弱密码算法</h4>
<ul>
<li>在使用加密算法时，不建议使用加密强度较弱的算法。</li>
</ul>
<p>错误例子：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">crypto/des，crypto/md5，crypto/sha1，crypto/rc4等。
</code></pre></td></tr></table>
</div>
</div><p><a id="1.1.7"></a></p>
<h3 id="17-正则表达式">1.7 正则表达式</h3>
<h4 id="171推荐使用regexp进行正则表达式匹配">1.7.1【推荐】使用regexp进行正则表达式匹配</h4>
<ul>
<li>正则表达式编写不恰当可被用于DoS攻击，造成服务不可用，推荐使用regexp包进行正则表达式匹配。regexp保证了线性时间性能和优雅的失败：对解析器、编译器和执行引擎都进行了内存限制。但regexp不支持以下正则表达式特性，如业务依赖这些特性，则regexp不适合使用。
<ul>
<li>回溯引用<a href="https://www.regular-expressions.info/backref.html" target="_blank" rel="noopener noreffer">Backreferences</a>和查看<a href="https://www.regular-expressions.info/lookaround.html" target="_blank" rel="noopener noreffer">Lookaround</a></li>
</ul>
</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// good
</span><span class="c1"></span><span class="nx">matched</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">regexp</span><span class="p">.</span><span class="nf">MatchString</span><span class="p">(</span><span class="s">`a.b`</span><span class="p">,</span> <span class="s">&#34;aaxbb&#34;</span><span class="p">)</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">matched</span><span class="p">)</span> <span class="c1">// true
</span><span class="c1"></span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>     <span class="c1">// nil (regexp is valid)
</span></code></pre></td></tr></table>
</div>
</div><p><a id="2"></a></p>
<h1 id="后台类">后台类</h1>
<p><a id="2.1"></a></p>
<h2 id="1-代码实现类-1">1 代码实现类</h2>
<p><a id="2.1.1"></a></p>
<h3 id="11-输入校验">1.1 输入校验</h3>
<h4 id="111必须按类型进行数据校验">1.1.1【必须】按类型进行数据校验</h4>
<ul>
<li>所有外部输入的参数，应使用<code>validator</code>进行白名单校验，校验内容包括但不限于数据长度、数据范围、数据类型与格式，校验不通过的应当拒绝</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// good
</span><span class="c1"></span><span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;fmt&#34;</span>
	<span class="s">&#34;github.com/go-playground/validator/v10&#34;</span>
<span class="p">)</span>

<span class="kd">var</span> <span class="nx">validate</span> <span class="o">*</span><span class="nx">validator</span><span class="p">.</span><span class="nx">Validate</span>
<span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span>
<span class="kd">func</span> <span class="nf">validateVariable</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">myEmail</span> <span class="o">:=</span> <span class="s">&#34;abc@tencent.com&#34;</span>
	<span class="nx">errs</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">myEmail</span><span class="p">,</span> <span class="s">&#34;required,email&#34;</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">errs</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">errs</span><span class="p">)</span>
		<span class="k">return</span>
        <span class="c1">//停止执行
</span><span class="c1"></span>	<span class="p">}</span>
	<span class="c1">// 验证通过，继续执行
</span><span class="c1"></span>    <span class="o">...</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><ul>
<li>无法通过白名单校验的应使用<code>html.EscapeString</code>、<code>text/template</code>或<code>bluemonday</code>对<code>&lt;, &gt;, &amp;, ',&quot;</code>等字符进行过滤或编码</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go">  <span class="kn">import</span><span class="p">(</span>
  	<span class="s">&#34;text/template&#34;</span>
  <span class="p">)</span>
  
  <span class="c1">// TestHTMLEscapeString HTML特殊字符转义
</span><span class="c1"></span>  <span class="kd">func</span> <span class="nf">main</span><span class="p">(</span><span class="nx">inputValue</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span><span class="p">{</span>
  	<span class="nx">escapedResult</span> <span class="o">:=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">HTMLEscapeString</span><span class="p">(</span><span class="nx">inputValue</span><span class="p">)</span>
  	<span class="k">return</span> <span class="nx">escapedResult</span>
  <span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.2"></a></p>
<h3 id="12-sql操作">1.2 SQL操作</h3>
<h4 id="121必须sql语句默认使用预编译并绑定变量">1.2.1【必须】SQL语句默认使用预编译并绑定变量</h4>
<ul>
<li>使用<code>database/sql</code>的prepare、Query或使用GORM等ORM执行SQL操作</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go">  <span class="kn">import</span> <span class="p">(</span>
    <span class="s">&#34;github.com/jinzhu/gorm&#34;</span>
    <span class="nx">_</span> <span class="s">&#34;github.com/jinzhu/gorm/dialects/sqlite&#34;</span>
  <span class="p">)</span>
  
  <span class="kd">type</span> <span class="nx">Product</span> <span class="kd">struct</span> <span class="p">{</span>
    <span class="nx">gorm</span><span class="p">.</span><span class="nx">Model</span>
    <span class="nx">Code</span> <span class="kt">string</span>
    <span class="nx">Price</span> <span class="kt">uint</span>
  <span class="p">}</span>
  <span class="o">...</span>
  <span class="kd">var</span> <span class="nx">product</span> <span class="nx">Product</span>
  <span class="nx">db</span><span class="p">.</span><span class="nf">First</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">product</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><ul>
<li>使用参数化查询，禁止拼接SQL语句，另外对于传入参数用于order by或表名的需要通过校验</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span>  <span class="kn">import</span> <span class="p">(</span>
  	<span class="s">&#34;database/sql&#34;</span>
  	<span class="s">&#34;fmt&#34;</span>
  	<span class="s">&#34;net/http&#34;</span>
  <span class="p">)</span>
  
  <span class="kd">func</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">db</span> <span class="o">*</span><span class="nx">sql</span><span class="p">.</span><span class="nx">DB</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
  	<span class="nx">q</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=&#39;%s&#39; ORDER BY PRICE&#34;</span><span class="p">,</span>
  		<span class="nx">req</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nf">Query</span><span class="p">()[</span><span class="s">&#34;category&#34;</span><span class="p">])</span>
  	<span class="nx">db</span><span class="p">.</span><span class="nf">Query</span><span class="p">(</span><span class="nx">q</span><span class="p">)</span>
  <span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">handlerGood</span><span class="p">(</span><span class="nx">db</span> <span class="o">*</span><span class="nx">sql</span><span class="p">.</span><span class="nx">DB</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">//使用?占位符
</span><span class="c1"></span>  	<span class="nx">q</span> <span class="o">:=</span> <span class="s">&#34;SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=&#39;?&#39; ORDER BY PRICE&#34;</span>
  	<span class="nx">db</span><span class="p">.</span><span class="nf">Query</span><span class="p">(</span><span class="nx">q</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nf">Query</span><span class="p">()[</span><span class="s">&#34;category&#34;</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.3"></a></p>
<h3 id="13-网络请求">1.3 网络请求</h3>
<h4 id="131必须资源请求过滤验证">1.3.1【必须】资源请求过滤验证</h4>
<ul>
<li>
<p>使用<code>&quot;net/http&quot;</code>下的方法<code>http.Get(url)</code>、<code>http.Post(url, contentType, body)</code>、<code>http.Head(url )</code>、<code>http.PostForm(url, data)</code>、<code>http.Do(req)</code>时，如变量值外部可控（指从参数中动态获取），应对请求目标进行严格的安全校验。</p>
</li>
<li>
<p>如请求资源域名归属固定的范围，如只允许<code>a.qq.com</code>和<code>b.qq.com</code>，应做白名单限制。如不适用白名单，则推荐的校验逻辑步骤是：</p>
<ul>
<li>
<p>第 1 步、只允许HTTP或HTTPS协议</p>
</li>
<li>
<p>第 2 步、解析目标URL，获取其HOST</p>
</li>
<li>
<p>第 3 步、解析HOST，获取HOST指向的IP地址转换成Long型</p>
</li>
<li>
<p>第 4 步、检查IP地址是否为内网IP，网段有：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">// 以RFC定义的专有网络为例，如有自定义私有网段亦应加入禁止访问列表。
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8
</code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>第 5 步、请求URL</p>
</li>
<li>
<p>第 6 步、如有跳转，跳转后执行1，否则绑定经校验的ip和域名，对URL发起请求</p>
</li>
</ul>
</li>
<li>
<p>官方库<code>encoding/xml</code>不支持外部实体引用，使用该库可避免xxe漏洞</p>
</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go">  <span class="kn">import</span> <span class="p">(</span>
  	<span class="s">&#34;encoding/xml&#34;</span>
  	<span class="s">&#34;fmt&#34;</span>
      <span class="s">&#34;os&#34;</span>
  <span class="p">)</span>
  
  <span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  	<span class="kd">type</span> <span class="nx">Person</span> <span class="kd">struct</span> <span class="p">{</span>
  		<span class="nx">XMLName</span>   <span class="nx">xml</span><span class="p">.</span><span class="nx">Name</span> <span class="s">`xml:&#34;person&#34;`</span>
  		<span class="nx">Id</span>        <span class="kt">int</span>      <span class="s">`xml:&#34;id,attr&#34;`</span>
  		<span class="nx">UserName</span> <span class="kt">string</span>   <span class="s">`xml:&#34;name&gt;first&#34;`</span>
  		<span class="nx">Comment</span> <span class="kt">string</span> <span class="s">`xml:&#34;,comment&#34;`</span>
  	<span class="p">}</span>
  
  	<span class="nx">v</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">Person</span><span class="p">{</span><span class="nx">Id</span><span class="p">:</span> <span class="mi">13</span><span class="p">,</span> <span class="nx">UserName</span><span class="p">:</span> <span class="s">&#34;John&#34;</span><span class="p">}</span>
  	<span class="nx">v</span><span class="p">.</span><span class="nx">Comment</span> <span class="p">=</span> <span class="s">&#34; Need more details. &#34;</span>
  
  	<span class="nx">enc</span> <span class="o">:=</span> <span class="nx">xml</span><span class="p">.</span><span class="nf">NewEncoder</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">)</span>
  	<span class="nx">enc</span><span class="p">.</span><span class="nf">Indent</span><span class="p">(</span><span class="s">&#34;  &#34;</span><span class="p">,</span> <span class="s">&#34;    &#34;</span><span class="p">)</span>
  	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">enc</span><span class="p">.</span><span class="nf">Encode</span><span class="p">(</span><span class="nx">v</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
  		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;error: %v\n&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
  	<span class="p">}</span>
  
  <span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.4"></a></p>
<h3 id="14-服务器端渲染">1.4 服务器端渲染</h3>
<h4 id="141必须模板渲染过滤验证">1.4.1【必须】模板渲染过滤验证</h4>
<ul>
<li>使用<code>text/template</code>或者<code>html/template</code>渲染模板时禁止将外部输入参数引入模板，或仅允许引入白名单内字符。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go">   <span class="c1">// bad
</span><span class="c1"></span>    <span class="kd">func</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">r</span><span class="p">.</span><span class="nf">ParseForm</span><span class="p">()</span>
      <span class="nx">x</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">)</span>
     
      <span class="kd">var</span> <span class="nx">tmpl</span> <span class="p">=</span> <span class="s">`&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;
</span><span class="s">    &lt;form action=&#34;/&#34; method=&#34;post&#34;&gt;
</span><span class="s">        First name:&lt;br&gt;
</span><span class="s">    &lt;input type=&#34;text&#34; name=&#34;name&#34; value=&#34;&#34;&gt;
</span><span class="s">    &lt;input type=&#34;submit&#34; value=&#34;Submit&#34;&gt;
</span><span class="s">    &lt;/form&gt;&lt;p&gt;`</span> <span class="o">+</span> <span class="nx">x</span> <span class="o">+</span> <span class="s">` &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;`</span>
    
      <span class="nx">t</span> <span class="o">:=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;main&#34;</span><span class="p">)</span>
      <span class="nx">t</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">tmpl</span><span class="p">)</span>
      <span class="nx">t</span><span class="p">.</span><span class="nf">Execute</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="s">&#34;Hello&#34;</span><span class="p">)</span>
    <span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span>    <span class="kn">import</span> <span class="p">(</span>
    	<span class="s">&#34;fmt&#34;</span>
    	<span class="s">&#34;github.com/go-playground/validator/v10&#34;</span>
    <span class="p">)</span>

    <span class="kd">var</span> <span class="nx">validate</span> <span class="o">*</span><span class="nx">validator</span><span class="p">.</span><span class="nx">Validate</span>
    <span class="nx">validate</span> <span class="p">=</span> <span class="nx">validator</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span>
    <span class="kd">func</span> <span class="nf">validateVariable</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
    	<span class="nx">errs</span> <span class="o">:=</span> <span class="nx">validate</span><span class="p">.</span><span class="nf">Var</span><span class="p">(</span><span class="nx">val</span><span class="p">,</span> <span class="s">&#34;gte=1,lte=100&#34;</span><span class="p">)</span><span class="c1">//限制必须是1-100的正整数
</span><span class="c1"></span>    	<span class="k">if</span> <span class="nx">errs</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
    		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">errs</span><span class="p">)</span>
    		<span class="k">return</span> <span class="nx">False</span>
    	<span class="p">}</span>
        <span class="k">return</span> <span class="nx">True</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">r</span><span class="p">.</span><span class="nf">ParseForm</span><span class="p">()</span>
        <span class="nx">x</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">)</span>
    
        <span class="k">if</span> <span class="nf">validateVariable</span><span class="p">(</span><span class="nx">x</span><span class="p">):</span>
            <span class="kd">var</span> <span class="nx">tmpl</span> <span class="p">=</span> <span class="s">`&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;
</span><span class="s">            &lt;form action=&#34;/&#34; method=&#34;post&#34;&gt;
</span><span class="s">            First name:&lt;br&gt;
</span><span class="s">            &lt;input type=&#34;text&#34; name=&#34;name&#34; value=&#34;&#34;&gt;
</span><span class="s">            &lt;input type=&#34;submit&#34; value=&#34;Submit&#34;&gt;
</span><span class="s">            &lt;/form&gt;&lt;p&gt;`</span> <span class="o">+</span> <span class="nx">x</span> <span class="o">+</span> <span class="s">` &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;`</span>
            <span class="nx">t</span> <span class="o">:=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;main&#34;</span><span class="p">)</span>
            <span class="nx">t</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">tmpl</span><span class="p">)</span>
            <span class="nx">t</span><span class="p">.</span><span class="nf">Execute</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="s">&#34;Hello&#34;</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="o">...</span>
    <span class="p">}</span>
    
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.5"></a></p>
<h3 id="15-web跨域">1.5 Web跨域</h3>
<h4 id="151必须跨域资源共享cors限制请求来源">1.5.1【必须】跨域资源共享CORS限制请求来源</h4>
<ul>
<li>CORS请求保护不当可导致敏感信息泄漏，因此应当严格设置Access-Control-Allow-Origin使用同源策略进行保护。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"> <span class="c1">// good
</span><span class="c1"></span>  <span class="nx">c</span> <span class="o">:=</span> <span class="nx">cors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">cors</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span>
      <span class="nx">AllowedOrigins</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;http://qq.com&#34;</span><span class="p">,</span> <span class="s">&#34;https://qq.com&#34;</span><span class="p">},</span>
      <span class="nx">AllowCredentials</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="nx">Debug</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
  <span class="p">})</span>
  
  <span class="c1">//引入中间件
</span><span class="c1"></span>  <span class="nx">handler</span> <span class="p">=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.6"></a></p>
<h3 id="16-响应输出">1.6 响应输出</h3>
<h4 id="161-必须设置正确的http响应包类型">1.6.1 【必须】设置正确的HTTP响应包类型</h4>
<ul>
<li>响应头Content-Type与实际响应内容，应保持一致。如：API响应数据类型是json，则响应头使用<code>application/json</code>；若为xml，则设置为<code>text/xml</code>。</li>
</ul>
<h4 id="162-必须添加安全响应头">1.6.2 【必须】添加安全响应头</h4>
<ul>
<li>所有接口、页面，添加响应头 <code>X-Content-Type-Options: nosniff</code>。</li>
<li>所有接口、页面，添加响应头<code>X-Frame-Options </code>。按需合理设置其允许范围，包括：<code>DENY</code>、<code>SAMEORIGIN</code>、<code>ALLOW-FROM origin</code>。用法参考：<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options" target="_blank" rel="noopener noreffer">MDN文档</a></li>
</ul>
<h4 id="163必须外部输入拼接到http响应头中需进行过滤">1.6.3【必须】外部输入拼接到HTTP响应头中需进行过滤</h4>
<ul>
<li>应尽量避免外部可控参数拼接到HTTP响应头中，如业务需要则需要过滤掉<code>\r</code>、<code>\n</code>等换行符，或者拒绝携带换行符号的外部输入。</li>
</ul>
<h4 id="164必须外部输入拼接到response页面前进行编码处理">1.6.4【必须】外部输入拼接到response页面前进行编码处理</h4>
<ul>
<li>直出html页面或使用模板生成html页面的，推荐使用<code>text/template</code>自动编码，或者使用<code>html.EscapeString</code>或<code>text/template</code>对<code>&lt;, &gt;, &amp;, ',&quot;</code>等字符进行编码。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span><span class="p">(</span>
	<span class="s">&#34;html/template&#34;</span>
<span class="p">)</span>        

<span class="kd">func</span> <span class="nf">outtemplate</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">param1</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nf">Query</span><span class="p">().</span><span class="nf">Get</span><span class="p">(</span><span class="s">&#34;param1&#34;</span><span class="p">)</span>
    <span class="nx">tmpl</span> <span class="o">:=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">)</span>
    <span class="nx">tmpl</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">tmpl</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="s">`</span><span class="cp">{{</span><span class="nx">define</span><span class="w"> </span><span class="s">&#34;T&#34;</span><span class="cp">}}{{</span><span class="na">.</span><span class="cp">}}{{</span><span class="k">end</span><span class="cp">}}</span><span class="s">`</span><span class="p">)</span>
    <span class="nx">tmpl</span><span class="p">.</span><span class="nf">ExecuteTemplate</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="s">&#34;T&#34;</span><span class="p">,</span> <span class="nx">param1</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.7"></a></p>
<h3 id="17-会话管理">1.7 会话管理</h3>
<h4 id="171必须安全维护session信息">1.7.1【必须】安全维护session信息</h4>
<ul>
<li>用户登录时应重新生成session，退出登录后应清理session。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;net/http&#34;</span>
	<span class="s">&#34;github.com/gorilla/mux&#34;</span>
	<span class="s">&#34;github.com/gorilla/handlers&#34;</span>
<span class="p">)</span>

<span class="c1">//创建cookie
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">setToken</span><span class="p">(</span><span class="nx">res</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">expireToken</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Add</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Minute</span> <span class="o">*</span> <span class="mi">30</span><span class="p">).</span><span class="nf">Unix</span><span class="p">()</span>
    <span class="nx">expireCookie</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Add</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Minute</span> <span class="o">*</span> <span class="mi">30</span><span class="p">)</span>
    <span class="o">...</span>
    <span class="nx">cookie</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Cookie</span><span class="p">{</span>
        <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;Auth&#34;</span><span class="p">,</span>
        <span class="nx">Value</span><span class="p">:</span> <span class="nx">signedToken</span><span class="p">,</span>
        <span class="nx">Expires</span><span class="p">:</span> <span class="nx">expireCookie</span><span class="p">,</span> <span class="c1">// 过期失效
</span><span class="c1"></span>        <span class="nx">HttpOnly</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
        <span class="nx">Path</span><span class="p">:</span> <span class="s">&#34;/&#34;</span><span class="p">,</span>
        <span class="nx">Domain</span><span class="p">:</span> <span class="s">&#34;127.0.0.1&#34;</span><span class="p">,</span>
        <span class="nx">Secure</span><span class="p">:</span> <span class="kc">true</span>
    <span class="p">}</span>

    <span class="nx">http</span><span class="p">.</span><span class="nf">SetCookie</span><span class="p">(</span><span class="nx">res</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">cookie</span><span class="p">)</span>
    <span class="nx">http</span><span class="p">.</span><span class="nf">Redirect</span><span class="p">(</span><span class="nx">res</span><span class="p">,</span> <span class="nx">req</span><span class="p">,</span> <span class="s">&#34;/profile&#34;</span><span class="p">,</span> <span class="mi">307</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 删除cookie
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">logout</span><span class="p">(</span><span class="nx">res</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">deleteCookie</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Cookie</span><span class="p">{</span>
        <span class="nx">Name</span><span class="p">:</span> <span class="s">&#34;Auth&#34;</span><span class="p">,</span>
        <span class="nx">Value</span><span class="p">:</span> <span class="s">&#34;none&#34;</span><span class="p">,</span>
        <span class="nx">Expires</span><span class="p">:</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span>
    <span class="p">}</span>
    <span class="nx">http</span><span class="p">.</span><span class="nf">SetCookie</span><span class="p">(</span><span class="nx">res</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">deleteCookie</span><span class="p">)</span>
    <span class="k">return</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="172必须csrf防护">1.7.2【必须】CSRF防护</h4>
<ul>
<li>涉及系统敏感操作或可读取敏感信息的接口应校验<code>Referer</code>或添加<code>csrf_token</code>。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// good
</span><span class="c1"></span><span class="kn">import</span> <span class="p">(</span>
    <span class="s">&#34;net/http&#34;</span>
    <span class="s">&#34;github.com/gorilla/csrf&#34;</span>
    <span class="s">&#34;github.com/gorilla/mux&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">r</span> <span class="o">:=</span> <span class="nx">mux</span><span class="p">.</span><span class="nf">NewRouter</span><span class="p">()</span>
    <span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/signup&#34;</span><span class="p">,</span> <span class="nx">ShowSignupForm</span><span class="p">)</span>
    <span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/signup/post&#34;</span><span class="p">,</span> <span class="nx">SubmitSignupForm</span><span class="p">)</span>
    <span class="c1">//使用csrf_token验证
</span><span class="c1"></span>    <span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8000&#34;</span><span class="p">,</span>
        <span class="nx">csrf</span><span class="p">.</span><span class="nf">Protect</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;32-byte-long-auth-key&#34;</span><span class="p">))(</span><span class="nx">r</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><a id="2.1.8"></a></p>
<h3 id="18-访问控制">1.8 访问控制</h3>
<h4 id="181必须默认鉴权">1.8.1【必须】默认鉴权</h4>
<ul>
<li>
<p>除非资源完全可对外开放，否则系统默认进行身份认证，使用白名单的方式放开不需要认证的接口或页面。</p>
</li>
<li>
<p>根据资源的机密程度和用户角色，以最小权限原则，设置不同级别的权限，如完全公开、登录可读、登录可写、特定用户可读、特定用户可写等</p>
</li>
<li>
<p>涉及用户自身相关的数据的读写必须验证登录态用户身份及其权限，避免越权操作</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="c1">-- 伪代码
</span><span class="c1"></span><span class="k">select</span> <span class="n">id</span> <span class="k">from</span> <span class="k">table</span> <span class="k">where</span> <span class="n">id</span><span class="o">=</span><span class="p">:</span><span class="n">id</span> <span class="k">and</span> <span class="n">userid</span><span class="o">=</span><span class="k">session</span><span class="p">.</span><span class="n">userid</span>
</code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>没有独立账号体系的外网服务使用<code>QQ</code>或<code>微信</code>登录，内网服务使用<code>统一登录服务</code>登录，其他使用账号密码登录的服务需要增加验证码等二次验证</p>
</li>
</ul>
<p><a id="2.1.9"></a></p>
<h3 id="19-并发保护">1.9 并发保护</h3>
<h4 id="191必须禁止在闭包中直接调用循环变量">1.9.1【必须】禁止在闭包中直接调用循环变量</h4>
<ul>
<li>在循环中启动协程，当协程中使用到了循环的索引值，由于多个协程同时使用同一个变量会产生数据竞争，造成执行结果异常。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">runtime</span><span class="p">.</span><span class="nf">GOMAXPROCS</span><span class="p">(</span><span class="nx">runtime</span><span class="p">.</span><span class="nf">NumCPU</span><span class="p">())</span>
    <span class="kd">var</span> <span class="nx">group</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>

    <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
        <span class="nx">group</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
        <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">defer</span> <span class="nx">group</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
            <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;%-2d&#34;</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="c1">//这里打印的i不是所期望的
</span><span class="c1"></span>        <span class="p">}()</span>
    <span class="p">}</span>
    <span class="nx">group</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
<span class="p">}</span>

<span class="c1">// good
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">runtime</span><span class="p">.</span><span class="nf">GOMAXPROCS</span><span class="p">(</span><span class="nx">runtime</span><span class="p">.</span><span class="nf">NumCPU</span><span class="p">())</span>
    <span class="kd">var</span> <span class="nx">group</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>

    <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
        <span class="nx">group</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
        <span class="k">go</span> <span class="kd">func</span><span class="p">(</span><span class="nx">j</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
                <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
                    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Recovered in start()&#34;</span><span class="p">)</span>
                <span class="p">}</span>
                <span class="nx">group</span><span class="p">.</span><span class="nf">Done</span><span class="p">()</span>
            <span class="p">}()</span>
        <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;%-2d&#34;</span><span class="p">,</span> <span class="nx">j</span><span class="p">)</span> <span class="c1">// 闭包内部使用局部变量
</span><span class="c1"></span>        <span class="p">}(</span><span class="nx">i</span><span class="p">)</span>  <span class="c1">// 把循环变量显式地传给协程
</span><span class="c1"></span>    <span class="p">}</span>
    <span class="nx">group</span><span class="p">.</span><span class="nf">Wait</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="192必须禁止并发写map">1.9.2【必须】禁止并发写map</h4>
<ul>
<li>并发写map容易造成程序崩溃并异常退出，建议加锁保护</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// bad
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">m</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="kt">int</span><span class="p">)</span>
	<span class="c1">//并发读写
</span><span class="c1"></span>	<span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
		<span class="k">for</span> <span class="p">{</span>
			<span class="nx">_</span> <span class="p">=</span> <span class="nx">m</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> 
		<span class="p">}</span>
	<span class="p">}()</span>
	<span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
		<span class="k">for</span> <span class="p">{</span>
			<span class="nx">m</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="p">=</span> <span class="mi">1</span>
		<span class="p">}</span>
	<span class="p">}()</span>
	<span class="k">select</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h4 id="193必须确保并发安全">1.9.3【必须】确保并发安全</h4>
<p>敏感操作如果未作并发安全限制，可导致数据读写异常，造成业务逻辑限制被绕过。可通过同步锁或者原子操作进行防护。</p>
<p>通过同步锁共享内存</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// good
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">count</span> <span class="kt">int</span>
<span class="kd">func</span> <span class="nf">Count</span><span class="p">(</span><span class="nx">lock</span> <span class="o">*</span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">lock</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span><span class="c1">// 加写锁
</span><span class="c1"></span>    <span class="nx">count</span><span class="o">++</span>
    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span>
    <span class="nx">lock</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span><span class="c1">// 解写锁，任何一个Lock()或RLock()均需要保证对应有Unlock()或RUnlock()
</span><span class="c1"></span><span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">lock</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span><span class="p">{}</span>
    <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
        <span class="k">go</span> <span class="nf">Count</span><span class="p">(</span><span class="nx">lock</span><span class="p">)</span> <span class="c1">//传递指针是为了防止函数内的锁和调用锁不一致
</span><span class="c1"></span>    <span class="p">}</span>
    <span class="k">for</span> <span class="p">{</span>
        <span class="nx">lock</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
        <span class="nx">c</span> <span class="o">:=</span> <span class="nx">count</span>
        <span class="nx">lock</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
        <span class="nx">runtime</span><span class="p">.</span><span class="nf">Gosched</span><span class="p">()</span><span class="c1">//交出时间片给协程
</span><span class="c1"></span>        <span class="k">if</span> <span class="nx">c</span> <span class="p">&gt;</span> <span class="mi">10</span> <span class="p">{</span>
            <span class="k">break</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><ul>
<li>使用<code>sync/atomic</code>执行原子操作</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// good
</span><span class="c1"></span><span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;sync&#34;</span>
	<span class="s">&#34;sync/atomic&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="kd">type</span> <span class="nx">Map</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span>
	<span class="kd">var</span> <span class="nx">m</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">Value</span>
	<span class="nx">m</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nb">make</span><span class="p">(</span><span class="nx">Map</span><span class="p">))</span>
	<span class="kd">var</span> <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span> <span class="c1">// used only by writers
</span><span class="c1"></span>	<span class="nx">read</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">val</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">m1</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Load</span><span class="p">().(</span><span class="nx">Map</span><span class="p">)</span>
		<span class="k">return</span> <span class="nx">m1</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span>
	<span class="p">}</span>
	<span class="nx">insert</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">val</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> <span class="c1">// 与潜在写入同步
</span><span class="c1"></span>		<span class="k">defer</span> <span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
		<span class="nx">m1</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Load</span><span class="p">().(</span><span class="nx">Map</span><span class="p">)</span> <span class="c1">// 导入struct当前数据
</span><span class="c1"></span>		<span class="nx">m2</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="nx">Map</span><span class="p">)</span>      <span class="c1">// 创建新值
</span><span class="c1"></span>		<span class="k">for</span> <span class="nx">k</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">m1</span> <span class="p">{</span>
			<span class="nx">m2</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="p">=</span> <span class="nx">v</span>
		<span class="p">}</span>
		<span class="nx">m2</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="p">=</span> <span class="nx">val</span>
		<span class="nx">m</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">m2</span><span class="p">)</span>   <span class="c1">// 用新的替代当前对象
</span><span class="c1"></span>	<span class="p">}</span>
	<span class="nx">_</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">read</span><span class="p">,</span> <span class="nx">insert</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div></div><div class="post-footer" id="post-footer">
    <div class="post-info">
        <div class="post-info-line">
            <div class="post-info-mod">
                <span>更新于 2021-01-25</span>
            </div>
            <div class="post-info-license"></div>
        </div>
        <div class="post-info-line">
            <div class="post-info-md"></div>
            <div class="post-info-share">
                <span><a href="javascript:void(0);" title="分享到 Twitter" data-sharer="twitter" data-url="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" data-title="《Go安全指南》" data-hashtags="Go"><i class="fab fa-twitter fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Facebook" data-sharer="facebook" data-url="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" data-hashtag="Go"><i class="fab fa-facebook-square fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Hacker News" data-sharer="hackernews" data-url="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" data-title="《Go安全指南》"><i class="fab fa-hacker-news fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Line" data-sharer="line" data-url="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" data-title="《Go安全指南》"><i data-svg-src="https://cdn.jsdelivr.net/npm/simple-icons@2.14.0/icons/line.svg"></i></a><a href="javascript:void(0);" title="分享到 微博" data-sharer="weibo" data-url="https://luckly.work/go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" data-title="《Go安全指南》" data-image="https://luckly007.oss-cn-beijing.aliyuncs.com/img/f77be1f477e980732d57475d3ddb69d.png"><i class="fab fa-weibo fa-fw"></i></a></span>
            </div>
        </div>
    </div>

    <div class="post-info-more">
        <section class="post-tags"><i class="fas fa-tags fa-fw"></i>&nbsp;<a href="/tags/go/">go</a></section>
        <section>
            <span><a href="javascript:void(0);" onclick="window.history.back();">返回</a></span>&nbsp;|&nbsp;<span><a href="/">主页</a></span>
        </section>
    </div>

    <div class="post-nav"><a href="/%E9%95%BF%E6%81%A8%E6%AD%8C/" class="prev" rel="prev" title="《长恨歌》"><i class="fas fa-angle-left fa-fw"></i>《长恨歌》</a>
            <a href="/%E5%A6%82%E4%BD%95%E5%88%B6%E5%AE%9A%E8%AE%A1%E5%88%92/" class="next" rel="next" title="《如何制定计划》">《如何制定计划》<i class="fas fa-angle-right fa-fw"></i></a></div>
</div>
</article></div>
            </main><footer class="footer">
        <div class="footer-container"><div class="footer-line">由 <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.79.1">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="noopener noreffer" title="LoveIt 0.2.10"><i class="far fa-kiss-wink-heart fa-fw"></i> LoveIt</a>
                </div><div class="footer-line"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2021</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/" target="_blank">早起的年轻人</a></span>&nbsp;|&nbsp;<span class="license"><a rel="license external nofollow noopener noreffer" href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">CC BY-NC 4.0</a></span></div>
        </div>
    </footer></div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="回到顶部">
                <i class="fas fa-arrow-up fa-fw"></i>
            </a><a href="#" id="view-comments" class="fixed-button" title="查看评论">
                <i class="fas fa-comment fa-fw"></i>
            </a>
        </div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cookieconsent@3.1.1/build/cookieconsent.min.css"><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/smooth-scroll@16.1.3/dist/smooth-scroll.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/autocomplete.js@0.37.1/dist/autocomplete.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/algoliasearch@4.2.0/dist/algoliasearch-lite.umd.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lazysizes@5.2.2/lazysizes.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sharer.js@0.4.0/sharer.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/typeit@7.0.4/dist/typeit.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/cookieconsent@3.1.1/build/cookieconsent.min.js"></script><script type="text/javascript">window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":10},"comment":{},"cookieconsent":{"content":{"dismiss":"同意","link":"了解更多","message":"本网站使用 Cookies 来改善您的浏览体验."},"enable":true,"palette":{"button":{"background":"#f0f0f0"},"popup":{"background":"#1aa3ff"}},"theme":"edgeless"},"data":{"id-1":"早起的年轻人","id-2":"早起的年轻人"},"search":{"algoliaAppID":"PASDMWALPK","algoliaIndex":"index.zh-cn","algoliaSearchKey":"b42948e51daaa93df92381c8e2ac0f93","highlightTag":"em","maxResultLength":10,"noResultsFound":"没有找到结果","snippetLength":50,"type":"algolia"},"typeit":{"cursorChar":"|","cursorSpeed":1000,"data":{"id-1":["id-1"],"id-2":["id-2"]},"duration":-1,"speed":100}};</script><script type="text/javascript" src="/js/theme.min.js"></script></body>
</html>
